package com.netty;

import com.google.protobuf.Message;
import com.netty.constant.Constants;
import com.netty.handler.ImSocketServerHandler;
import com.netty.model.proto.MessageBodyProto;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ThreadFactory;

/**
 * @author zl
 * @version 1.0
 * @date 2021/9/15 8:59
 */
public class ImSocketServer {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    private ChannelGroup channelGroup;
    private ProtobufDecoder decoder = new ProtobufDecoder(MessageBodyProto.Model.getDefaultInstance());
    private ProtobufEncoder encoder = new ProtobufEncoder();


    private CIMSocketRequestHandler cimRequestHandler;

    private int port;

    public ImSocketServer(int port,CIMSocketRequestHandler cimRequestHandler) {
        this.port = port;
        this.cimRequestHandler = cimRequestHandler;
    }
    public ImSocketServer(int port,CIMSocketRequestHandler cimRequestHandler,ChannelGroup channelGroup) {
        this.port = port;
        this.cimRequestHandler = cimRequestHandler;
        this.channelGroup = channelGroup;
    }

    /**
     * 启动netty socket
     *
     * @throws InterruptedException
     */
    public void startServer() throws InterruptedException {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>start socket ...<<<<<<<<<<<<<<<<<<<<<<");
        createEventGroup();
        // Server 服务启动
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);//多个端口号重用同一地址
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                //日志输出
                //pipeline.addLast(new LoggingHandler());
                // 主要用于处理大数据流，比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的; 增加之后就不用考虑这个问题了
                pipeline.addLast(new ChunkedWriteHandler());

                pipeline.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
                pipeline.addLast(decoder);
                pipeline.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
                pipeline.addLast(encoder);
                pipeline.addLast(new IdleStateHandler(Constants.ImserverConfig.READ_IDLE_TIME, Constants.ImserverConfig.WRITE_IDLE_TIME, 0));
                pipeline.addLast("handler", new ImSocketServerHandler(cimRequestHandler,channelGroup));
            }
        });

        ChannelFuture channelFuture = bootstrap.bind(port).syncUninterruptibly();
        channelFuture.channel().newSucceededFuture().addListener(future -> {
            String logBanner = "\n\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "*                   Socket Server started on port {}.                               *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n";
            logger.info(logBanner, port);
        });
        channelFuture.channel().closeFuture().addListener(future -> this.destroy(bossGroup, workerGroup));
    }

    /**
     * 销毁队列
     */
    public void destroy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>destroy server<<<<<<<<<<<<<<<<<<<<<<");
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        logger.info(">>>>>>>>>>>>>>>>>>>>>>destroy server complate.<<<<<<<<<<<<<<<<<<<<<<");
    }

    /**
     * 根据系统信息创建对应的队列
     */
    private void createEventGroup() {
        ThreadFactory bossThreadFactory = r -> {
            Thread thread = new Thread(r);
            thread.setName("nio-boss-boss");
            return thread;
        };
        ThreadFactory workerThreadFactory = r -> {
            Thread thread = new Thread(r);
            thread.setName("nio-worker-work");
            return thread;
        };
        if (isLinuxSystem()) {
            bossGroup = new EpollEventLoopGroup();
            workerGroup = new EpollEventLoopGroup();
        } else {
            bossGroup = new NioEventLoopGroup();
            workerGroup = new NioEventLoopGroup();
        }
    }

    /**
     * 判断是否是linux
     *
     * @return
     */
    private boolean isLinuxSystem() {
        String osName = System.getProperty("os.name").toLowerCase();
        return false;
    }

}
